package Linux五种IO模型.reactor1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author coderFu
 * @create 2022/3/6
 * 服务端的建立
 */
public class ReactorDemo {

    private static Selector selector;

    private static ServerSocketChannel serverSocketChannel;

    static {
        // 完成 相关管道的初始化工作
        try {
            selector =  Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public ReactorDemo(int port) throws IOException {
        // 绑定端口
        serverSocketChannel.bind(new InetSocketAddress(port));

        // 将服务端的管道注册到选择器中
        SelectionKey register = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 服务端的key附件一个 Acceptor对象，来完成与客户端的连接操作
        register.attach(new AcceptDemo(serverSocketChannel,selector));

    }







    public void reactor() throws IOException {
        // 不断监听是否有可读的管道
        // 在线程被中断前持续运行
        while (!Thread.interrupted()){
            if (selector.select() == 0){
                System.out.println("没有连接进入");
                continue;
            }
            // 获取所有的就绪事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()){
                // 进行调度分发 、根据不同的key选择不同的方法进行处理
                dispatch(iterator.next());
                iterator.remove();
            }
        }
    }

    public void dispatch(SelectionKey key){

        // 根据事件key 绑定来开启线程
        // 如果是服务端的就绪事件，此时获取的就是Acceptor对象
        // 如果是客户端的就绪事件，此时获取到的就是 Handler来处理客户端的读写
        //因为 客户端和服务端绑定的对象不同，所以可以在此做一个区分
        // 在 Handler 客户的读写里面，可以根据管道的数据情况，来进行数据的读取还是
        // 数据的写入
        Runnable r  = (Runnable) key.attachment();
        // 三个绑定，服务端启动的， 使用绑定 accept 客户端链接的时候使用

        // 客户端链接的时候绑定 read
        // 客户输入的时候绑定 write
        // 输出给客户端的时候 read

        if (r != null){
            // 调用Acceptor中的run（）方法
            r.run();
        }
    }
}
